package ruteodosciudades;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import ruteodosciudades.algoritmo.extra.Arista;
import ruteodosciudades.algoritmo.extra.ManageDB;
import ruteodosciudades.algoritmo.extra.Nodo;
import ruteodosciudades.algoritmo.extra.NodoImg;

/**
 *
 * @author markos
 */
public class GraphCanvas extends Canvas implements Runnable {

    private final int NODOSIZE = 20;
    private NodoImg startNodo;
    private NodoImg endNodo;
    private List<NodoImg> listaNodos;
    private List<NodoImg> listaNodosCamino;
    private ManageDB manageDb;
    private long tiempo;

    public long getTiempo() {
        return tiempo;
    }

    public void setTiempo(long tiempo) {
        this.tiempo = tiempo;
    }

    @Override
    public void paint(Graphics g) {
        dibujaAristas(g);
        dibujaNodos(g);
        dibujaDatos(g);
    }

    private void dibujaDatos(Graphics g) {
        g.setColor(Color.lightGray);
        g.fillRect(0, 0, 600, 35);
        g.setColor(Color.black);
        g.setFont(new Font("Arial", Font.BOLD, 10));
        g.drawString("Tiempo (milisegundos) : " + tiempo, 10, 15);
        if (endNodo == null || startNodo == null) {
            g.drawString("Ruta : No existe", 10, 20);
        } else {
            try {
                String path = System.getProperty("user.dir");
                File archivo = new File(path + "/salida.txt");
                if (archivo.exists()) {
                    archivo.createNewFile();
                }

                PrintWriter pw = new PrintWriter(archivo);
                pw.println("Ruta : " + startNodo.getNodo().name + getPath(endNodo.getNodo(), ""));
                pw.println("Tiempo (milisegundos) : " + tiempo);
                pw.close();

            } catch (Exception e) {
                e.printStackTrace();
            }
            g.drawString("Ruta : " + startNodo.getNodo().name + getPath(endNodo.getNodo(), ""), 10, 30);
        }
    }

    private String getPath(Nodo dest, String path) {
        if (dest.prev != null) {
            getPath(dest.prev, path);
            path += " a ";
        }
        path += dest.name;
        return path;
    }

    /**
     * Metodo publico llamado para el repintado del canvas
     *
     */
    public void doGrafo() {
        repaint();
    }

    /**
     * Dibuja todos los nodos del grafo sobre las aristas dibujadas, tambien
     * escribe el costo de cada nodo.
     *
     * @param g
     */
    private void dibujaNodos(Graphics g) {
        g.setFont(new Font("Arial", Font.BOLD, 10));
        for (NodoImg n : listaNodos) {
            g.setColor(Color.GRAY);

            if (startNodo != null && n.equals(startNodo)) {
                g.setColor(Color.green);
            } else if (endNodo != null && n.equals(endNodo) && listaNodosCamino.size() > 1) {
                g.setColor(Color.green);
            } else if (findNodoInPath(n) && listaNodosCamino.size() > 1) {
                g.setColor(Color.orange);
            }
            g.fillOval(n.getX() - NODOSIZE / 2, n.getY() - NODOSIZE / 2, NODOSIZE, NODOSIZE);
            g.setColor(Color.BLACK);
            g.drawString(n.getNodo().getName() + " ( " + new Double(n.getNodo().dist).intValue() + " )", n.getX() + NODOSIZE / 2, n.getY() - NODOSIZE / 2);
        }
    }

    /**
     * Dibuja todas las aristas del grafo.
     *
     * @param g
     */
    private void dibujaAristas(Graphics g) {
        // se actualizan las aristas para el grafo completo
        System.out.println("dibujaAristas");
        int origenX, origenY, destinoX, destinoY;
        for (NodoImg n : listaNodos) {
            origenX = n.getX();
            origenY = n.getY();

            boolean isNodePath = findNodoInPath(n);
            // se recorren las aristas de cada nodo
            for (Arista a : n.getNodo().adj) {
                destinoX = findNodoInGraph(a.dest).getX();
                destinoY = findNodoInGraph(a.dest).getY();
                if (a.cost > 0) {
                    g.setColor(Color.BLACK);
                    if (isNodePath) {
                        if (findAristaInPath(a, n)) {
                            g.setColor(Color.orange); // se pinta naranja si es camino
                        }
                    }
                    g.drawLine(origenX, origenY, destinoX, destinoY);
                }
            }
        }
    }

    /**
     * Setea los datos fundamentales para el dibujado del grafo.
     *
     * @param mapNodos Tabla del grafo.
     * @param listaNodes Lista completa de nodos.
     * @param listaNodosPath Lista de nodos del camino mas corto.
     * @param iniNode  Nombre del nodo de inicio.
     * @param finNode Nombre del nodo meta.
     */
    public void setNodos(Map<String, Nodo> mapNodos, String iniNode, String finNode) {
        this.listaNodos = buildGrafoCoordenado(mapNodos, iniNode, finNode);
        System.out.println("Cantidad de nodos en el grafo : " + listaNodos.size());
        listaNodosCamino = new ArrayList<NodoImg>();
        if (endNodo != null) {
            buildCamino(mapNodos.get(finNode), this.listaNodosCamino);
        }
        System.out.println("Cantidad de nodos en el camino : " + listaNodosCamino.size());
    }

    private List<NodoImg> buildGrafoCoordenado(Map<String, Nodo> map, String iniNode, String finNode) {
        List<NodoImg> listCoordenada = new ArrayList<NodoImg>();
        Nodo node = null;
        for (String idNodo : manageDb.getListaIdNodos()) {
            node = map.get(idNodo);
            if (node != null) {
                listCoordenada.add(manageDb.updateNodoCoordenado(node));
            }
        }
        node = map.get(iniNode);
        if (node != null) {
            this.startNodo = manageDb.updateNodoCoordenado(map.get(iniNode));
        }
        System.out.println(startNodo.getNodo());
        node = map.get(finNode);
        if (node != null) {
            this.endNodo = manageDb.updateNodoCoordenado(map.get(finNode));
            System.out.println(endNodo.getNodo());
        }

        return listCoordenada;
    }

    private void buildCamino(Nodo dest, List<NodoImg> lista) {
        if (dest.prev != null) {
            buildCamino(dest.prev, lista);
            System.out.println(dest.toString());
        }
        lista.add(manageDb.updateNodoCoordenado(dest));
    }

    /**
     * Averigua si una arista pertenece al camino mas corto en este nodo.
     *
     * @param edge
     * @param nodoInPath
     * @return
     */
    private boolean findAristaInPath(Arista edge, NodoImg nodoInPath) {
        boolean retorno = false;
        for (Arista a : nodoInPath.getNodo().adj) {
            if (a != null) {
                if (findNodoInPath(new NodoImg(edge.dest, 0, 0))) {

                    if (edge.dest.prev == null) {
                        continue;
                    }
                    if (edge.dest.prev.equals(nodoInPath.getNodo())) {
                        retorno = true;
                        break;
                    }
                }
            }
        }
        return retorno;
    }

    /**
     * Averigua si un nodo pertenece al camino mas corto
     *
     * @param findNodo
     * @return
     */
    private boolean findNodoInPath(NodoImg findNodo) {
        boolean retorno = false;
        for (NodoImg n : listaNodosCamino) {
            if (n.equals(findNodo)) {
                retorno = true;
                break;
            }
        }
        return retorno;
    }

    /**
     * Averigua si un nodo pertenece al camino mas corto
     *
     * @param findNodo
     * @return
     */
    private NodoImg findNodoInGraph(Nodo nodo) {
        NodoImg retorno = null;

        for (NodoImg n : listaNodos) {
            if (n.getNodo().equals(nodo)) {
                retorno = n;
                break;
            }
        }
        return retorno;
    }

    @Override
    public void run() {
    }

    /**
     * Constructor de la clase.
     *
     */
    public GraphCanvas(String database) {
        manageDb = new ManageDB(database);
        setBackground(Color.white);
        setSize(800, 600);
    }
}
